import { dlopen, FFIType, ptr,CString } from "bun:ffi";

/**
 * 转换字符串为C字符串
 * @param value 字符串
 * @returns const char*
 */
export function toCStr(value: string) {
    const str = Buffer.from(value + "\0", "utf8");
    return ptr(str);
}

let lib_file: any

if (process.env.RAYLIB_PATH) {
    lib_file = { default: process.env.RAYLIB_PATH }
} else if (process.platform === "win32" && process.arch === "x64") { // 判断是否为 Windows 64 位系统
    //@ts-expect-error 忽略类型错误
    lib_file = await import("../lib/windows/x64/raylib.dll")
}

export const lib = dlopen(lib_file.default, {
    InitWindow: {
        args: [FFIType.i32, FFIType.i32, FFIType.cstring],
        returns: FFIType.void
    },
    CloseWindow: {
        args: [],
        returns: FFIType.void
    },
    WindowShouldClose: {
        args: [],
        returns: FFIType.bool
    },
    IsWindowReady: {
        args: [],
        returns: FFIType.bool
    },
    IsWindowFullscreen: {
        args: [],
        returns: FFIType.bool
    },
    IsWindowHidden: {
        args: [],
        returns: FFIType.bool
    },
    IsWindowMinimized: {
        args: [],
        returns: FFIType.bool
    },
    IsWindowMaximized: {
        args: [],
        returns: FFIType.bool
    },
    IsWindowFocused: {
        args: [],
        returns: FFIType.bool
    },
    IsWindowResized: {
        args: [],
        returns: FFIType.bool
    },
    IsWindowState: {
        args: [FFIType.i32],
        returns: FFIType.bool
    },
    SetWindowState: {
        args: [FFIType.i32],
        returns: FFIType.void
    },
    ClearWindowState: {
        args: [FFIType.i32],
        returns: FFIType.void
    },
    ToggleFullscreen: {
        args: [],
        returns: FFIType.void
    },
    ToggleBorderlessWindowed: {
        args: [],
        returns: FFIType.void
    },
    MaximizeWindow: {
        args: [],
        returns: FFIType.void
    },
    MinimizeWindow: {
        args: [],
        returns: FFIType.void
    },
    RestoreWindow: {
        args: [],
        returns: FFIType.void
    },
    SetWindowIcon: {
        args: [FFIType.ptr],
        returns: FFIType.void
    },
    SetWindowIcons: {
        args: [FFIType.ptr, FFIType.i32],
        returns: FFIType.void,
    },
    SetWindowTitle: {
        args: [FFIType.cstring],
        returns: FFIType.void
    },
    SetWindowPosition: {
        args: [FFIType.i32, FFIType.i32],
        returns: FFIType.void
    },
    SetWindowMonitor: {
        args: [FFIType.i32],
        returns: FFIType.void
    },
    SetWindowMinSize: {
        args: [FFIType.i32, FFIType.i32],
        returns: FFIType.void
    },
    SetWindowMaxSize: {
        args: [FFIType.i32, FFIType.i32],
        returns: FFIType.void
    },
    SetWindowSize: {
        args: [FFIType.i32, FFIType.i32],
        returns: FFIType.void
    },
    SetWindowOpacity: {
        args: [FFIType.f32],
        returns: FFIType.void
    },
    SetWindowFocused: {
        args: [],
        returns: FFIType.void
    },
    GetWindowHandle: {
        args: [],
        returns: FFIType.ptr
    },
    GetScreenWidth: {
        args: [],
        returns: FFIType.i32
    },
    GetScreenHeight: {
        args: [],
        returns: FFIType.i32
    },
    GetRenderWidth: {
        args: [],
        returns: FFIType.i32
    },
    GetRenderHeight: {
        args: [],
        returns: FFIType.i32
    },
    GetMonitorCount: {
        args: [],
        returns: FFIType.i32
    },
    GetCurrentMonitor: {
        args: [],
        returns: FFIType.i32
    },
    GetMonitorPosition: {
        args: [FFIType.i32],
        returns: FFIType.ptr,
    },
    GetMonitorWidth: {
        args: [FFIType.i32],
        returns: FFIType.i32,
    },
    GetMonitorHeight: {
        args: [FFIType.i32],
        returns: FFIType.i32,
    },
    GetMonitorPhysicalWidth: {
        args: [FFIType.i32],
        returns: FFIType.i32,
    },
    GetMonitorPhysicalHeight: {
        args: [FFIType.i32],
        returns: FFIType.i32,
    },
    GetMonitorRefreshRate: {
        args: [FFIType.i32],
        returns: FFIType.i32,
    },
    GetWindowPosition: {
        args: [],
        returns: FFIType.ptr,
    },
    GetWindowScaleDPI: {
        args: [],
        returns: FFIType.ptr
    },
    GetMonitorName: {
        args: [FFIType.i32],
        returns: FFIType.cstring
    },
    SetClipboardText: {
        args: [FFIType.cstring],
        returns: FFIType.void
    },
    GetClipboardText: {
        args: [],
        returns: FFIType.cstring
    },
    GetClipboardImage: {
        args: [],
        returns: FFIType.ptr
    },
    EnableEventWaiting: {
        args: [],
        returns: FFIType.void
    },
    DisableEventWaiting: {
        args: [],
        returns: FFIType.void
    },
    ShowCursor: {
        args: [],
        returns: FFIType.void
    },
    HideCursor: {
        args: [],
        returns: FFIType.void
    },
    IsCursorHidden: {
        args: [],
        returns: FFIType.bool
    },
    EnableCursor: {
        args: [],
        returns: FFIType.void
    },
    DisableCursor: {
        args: [],
        returns: FFIType.void
    },
    IsCursorOnScreen: {
        args: [],
        returns: FFIType.bool
    },
    ClearBackground: {
        args: [FFIType.ptr],
        returns: FFIType.void
    },
    BeginDrawing: {
        args: [],
        returns: FFIType.void
    },
    EndDrawing: {
        args: [],
        returns: FFIType.void
    },
    BeginMode2D: {
        args: [FFIType.ptr],
        returns: FFIType.void
    },
    EndMode2D: {
        args: [],
        returns: FFIType.void
    },
    BeginMode3D: {
        args: [FFIType.ptr],
        returns: FFIType.void
    },
    EndMode3D: {
        args: [],
        returns: FFIType.void
    },
    BeginTextureMode: {
        args: [FFIType.ptr],
        returns: FFIType.void
    },
    EndTextureMode: {
        args: [],
        returns: FFIType.void
    },
    BeginShaderMode: {
        args: [FFIType.ptr],
        returns: FFIType.void
    },
    EndShaderMode: {
        args: [],
        returns: FFIType.void
    },
    BeginBlendMode: {
        args: [FFIType.i32],
        returns: FFIType.void
    },
    EndBlendMode: {
        args: [],
        returns: FFIType.void
    },
    BeginScissorMode: {
        args: [FFIType.i32, FFIType.i32, FFIType.i32, FFIType.i32],
        returns: FFIType.void
    },
    EndScissorMode: {
        args: [],
        returns: FFIType.void
    },
    BeginVrStereoMode: {
        args: [FFIType.ptr],
        returns: FFIType.void
    },
    EndVrStereoMode: {
        args: [],
        returns: FFIType.void
    },
    LoadVrStereoConfig: {
        args: [FFIType.ptr],
        returns: FFIType.ptr
    },
    UnloadVrStereoConfig: {
        args: [FFIType.ptr],
        returns: FFIType.void
    },
    LoadShader: {
        args: [FFIType.cstring, FFIType.cstring],
        returns: FFIType.ptr
    },
    LoadShaderFromMemory: {
        args: [FFIType.cstring, FFIType.cstring],
        returns: FFIType.ptr
    },
    IsShaderValid: {
        args: [FFIType.ptr],
        returns: FFIType.bool
    },
    GetShaderLocation: {
        args: [FFIType.ptr, FFIType.cstring],
        returns: FFIType.i32,
    },
    GetShaderLocationAttrib: {
        args: [FFIType.ptr, FFIType.cstring],
        returns: FFIType.i32,
    },
    SetShaderValue: {
        args: [FFIType.ptr, FFIType.i32, FFIType.ptr, FFIType.i32],
        returns: FFIType.void,
    },
    SetShaderValueV: {
        args: [FFIType.ptr, FFIType.i32, FFIType.ptr, FFIType.i32, FFIType.i32],
        returns: FFIType.void,
    },
    SetShaderValueMatrix: {
        args: [FFIType.ptr, FFIType.i32, FFIType.ptr],
        returns: FFIType.void,
    },
    SetShaderValueTexture: {
        args: [FFIType.ptr, FFIType.i32, FFIType.ptr],
        returns: FFIType.void,
    },
    UnloadShader: {
        args: [FFIType.ptr],
        returns: FFIType.void,
    },
    GetScreenToWorldRay: {
        args: [FFIType.ptr, FFIType.ptr],
        returns: FFIType.ptr,
    },
    GetScreenToWorldRayEx: {
        args: [FFIType.ptr, FFIType.ptr, FFIType.i32, FFIType.i32],
        returns: FFIType.ptr,
    },
    GetWorldToScreen: {
        args: [FFIType.ptr, FFIType.ptr],
        returns: FFIType.ptr,
    },
    GetWorldToScreenEx: {
        args: [FFIType.ptr, FFIType.ptr, FFIType.i32, FFIType.i32],
        returns: FFIType.ptr,
    },
    GetWorldToScreen2D: {
        args: [FFIType.ptr, FFIType.ptr],
        returns: FFIType.ptr,
    },
    GetScreenToWorld2D: {
        args: [FFIType.ptr, FFIType.ptr],
        returns: FFIType.ptr,
    },
    GetCameraMatrix: {
        args: [FFIType.ptr],
        returns: FFIType.ptr,
    },
    GetCameraMatrix2D: {
        args: [FFIType.ptr],
        returns: FFIType.ptr,
    },
    SetTargetFPS: {
        args: [FFIType.i32],
        returns: FFIType.void,
    },
    GetFrameTime: {
        args: [],
        returns: FFIType.f32,
    },
    GetTime: {
        args: [],
        returns: FFIType.double,
    },
    GetFPS: {
        args: [],
        returns: FFIType.i32,
    },
    SwapScreenBuffer: {
        args: [],
        returns: FFIType.void,
    },
    PollInputEvents: {
        args: [],
        returns: FFIType.void
    },
    WaitTime: {
        args: [FFIType.double],
        returns: FFIType.void
    },
    SetRandomSeed: {
        args: [FFIType.i32],
        returns: FFIType.void
    },
    GetRandomValue: {
        args: [FFIType.i32, FFIType.i32],
        returns: FFIType.i32
    },
    LoadRandomSequence: {
        args: [FFIType.i32, FFIType.i32, FFIType.i32],
        returns: FFIType.ptr
    },
    UnloadRandomSequence: {
        args: [FFIType.ptr],
        returns: FFIType.void
    },
    TakeScreenshot: {
        args: [FFIType.cstring],
        returns: FFIType.void
    },
    SetConfigFlags: {
        args: [FFIType.i32],
        returns: FFIType.void
    },
    OpenURL: {
        args: [FFIType.cstring],
        returns: FFIType.void
    },
    TraceLog: {
        args: [FFIType.i32, FFIType.cstring],
        returns: FFIType.void
    },
    SetTraceLogLevel: {
        args: [FFIType.i32],
        returns: FFIType.void
    },
    MemAlloc: {
        args: [FFIType.i32],
        returns: FFIType.ptr
    },
    MemRealloc: {
        args: [FFIType.ptr, FFIType.i32],
        returns: FFIType.ptr
    },
    MemFree: {
        args: [FFIType.ptr],
        returns: FFIType.void
    },
    SetTraceLogCallback: {
        args: [FFIType.ptr],
        returns: FFIType.void
    },
    SetLoadFileDataCallback: {
        args: [FFIType.ptr],
        returns: FFIType.void
    },
    SetSaveFileDataCallback: {
        args: [FFIType.ptr],
        returns: FFIType.void
    },
    SetLoadFileTextCallback: {
        args: [FFIType.ptr],
        returns: FFIType.void
    },
    SetSaveFileTextCallback: {
        args: [FFIType.ptr],
        returns: FFIType.void
    },
    LoadFileData: {
        args: [FFIType.cstring, FFIType.i32],
        returns: FFIType.ptr
    },
    UnloadFileData: {
        args: [FFIType.ptr],
        returns: FFIType.void
    },
    SaveFileData: {
        args: [FFIType.cstring, FFIType.ptr, FFIType.i32],
        returns: FFIType.bool
    },
    ExportDataAsCode: {
        args: [FFIType.ptr, FFIType.i32, FFIType.cstring],
        returns: FFIType.bool
    },
    LoadFileText: {
        args: [FFIType.cstring],
        returns: FFIType.cstring
    },
    UnloadFileText: {
        args: [FFIType.cstring],
        returns: FFIType.void
    },
    SaveFileText: {
        args: [FFIType.cstring, FFIType.cstring],
        returns: FFIType.bool
    },
    FileExists: {
        args: [FFIType.cstring],
        returns: FFIType.bool
    },
    DirectoryExists: {
        args: [FFIType.cstring],
        returns: FFIType.bool
    },
    IsFileExtension: {
        args: [FFIType.cstring, FFIType.cstring],
        returns: FFIType.bool
    },
    GetFileLength: {
        args: [FFIType.cstring],
        returns: FFIType.i32
    },
    GetFileExtension: {
        args: [FFIType.cstring],
        returns: FFIType.cstring
    },
    GetFileName: {
        args: [FFIType.cstring],
        returns: FFIType.cstring
    },
    GetFileNameWithoutExt: {
        args: [FFIType.cstring],
        returns: FFIType.cstring
    },
    GetDirectoryPath: {
        args: [FFIType.cstring],
        returns: FFIType.cstring
    },
    GetPrevDirectoryPath: {
        args: [FFIType.cstring],
        returns: FFIType.cstring,
    },
    GetWorkingDirectory: {
        args: [],
        returns: FFIType.cstring,
    },
    GetApplicationDirectory: {
        args: [],
        returns: FFIType.cstring,
    },
    MakeDirectory: {
        args: [FFIType.cstring],
        returns: FFIType.i32,
    },
    ChangeDirectory: {
        args: [FFIType.cstring],
        returns: FFIType.bool,
    },
    IsPathFile: {
        args: [FFIType.cstring],
        returns: FFIType.bool,
    },
    IsFileNameValid: {
        args: [FFIType.cstring],
        returns: FFIType.bool,
    },
    LoadDirectoryFiles: {
        args: [FFIType.cstring],
        returns: FFIType.ptr,
    },
    LoadDirectoryFilesEx: {
        args: [FFIType.cstring, FFIType.cstring, FFIType.bool],
        returns: FFIType.ptr,
    },
    UnloadDirectoryFiles: {
        args: [FFIType.ptr],
        returns: FFIType.void,
    },
    IsFileDropped: {
        args: [],
        returns: FFIType.bool,
    },
    LoadDroppedFiles: {
        args: [],
        returns: FFIType.ptr,
    },
    UnloadDroppedFiles: {
        args: [FFIType.ptr],
        returns: FFIType.void,
    },
    GetFileModTime: {
        args: [FFIType.cstring],
        returns: FFIType.double,
    },
    CompressData: {
        args: [FFIType.cstring, FFIType.i32, FFIType.ptr],
        returns: FFIType.cstring
    },
    DecompressData: {
        args: [FFIType.cstring, FFIType.i32, FFIType.ptr],
        returns: FFIType.cstring
    },
    EncodeDataBase64: {
        args: [FFIType.cstring, FFIType.i32, FFIType.ptr],
        returns: FFIType.cstring
    },
    DecodeDataBase64: {
        args: [FFIType.cstring, FFIType.ptr],
        returns: FFIType.cstring
    },
    ComputeCRC32: {
        args: [FFIType.cstring, FFIType.i32],
        returns: FFIType.i32
    },
    ComputeMD5: {
        args: [FFIType.cstring, FFIType.i32],
        returns: FFIType.ptr
    },
    ComputeSHA1: {
        args: [FFIType.cstring, FFIType.i32],
        returns: FFIType.ptr
    },
    LoadAutomationEventList: {
        args: [FFIType.cstring],
        returns: FFIType.ptr,
    },
    UnloadAutomationEventList: {
        args: [FFIType.ptr],
        returns: FFIType.void,
    },
    ExportAutomationEventList: {
        args: [FFIType.ptr, FFIType.cstring],
        returns: FFIType.bool,
    },
    SetAutomationEventList: {
        args: [FFIType.ptr],
        returns: FFIType.void,
    },
    SetAutomationEventBaseFrame: {
        args: [FFIType.i32],
        returns: FFIType.void,
    },
    StartAutomationEventRecording: {
        args: [],
        returns: FFIType.void,
    },
    StopAutomationEventRecording: {
        args: [],
        returns: FFIType.void,
    },
    PlayAutomationEvent: {
        args: [FFIType.ptr],
        returns: FFIType.void,
    },
    IsKeyPressed: {
        args: [FFIType.i32],
        returns: FFIType.bool,
    },
    IsKeyPressedRepeat: {
        args: [FFIType.i32],
        returns: FFIType.bool,
    },
    IsKeyDown: {
        args: [FFIType.i32],
        returns: FFIType.bool,
    },
    IsKeyReleased: {
        args: [FFIType.i32],
        returns: FFIType.bool,
    },
    IsKeyUp: {
        args: [FFIType.i32],
        returns: FFIType.bool,
    },
    GetKeyPressed: {
        args: [],
        returns: FFIType.i32,
    },
    GetCharPressed: {
        args: [],
        returns: FFIType.i32,
    },
    SetExitKey: {
        args: [FFIType.i32],
        returns: FFIType.void,
    },
    IsGamepadAvailable: {
        args: [FFIType.i32],
        returns: FFIType.bool,
    },
    GetGamepadName: {
        args: [FFIType.i32],
        returns: FFIType.cstring,
    },
    IsGamepadButtonPressed: {
        args: [FFIType.i32, FFIType.i32],
        returns: FFIType.bool,
    },
    IsGamepadButtonDown: {
        args: [FFIType.i32, FFIType.i32],
        returns: FFIType.bool,
    },
    IsGamepadButtonReleased: {
        args: [FFIType.i32, FFIType.i32],
        returns: FFIType.bool,
    },
    IsGamepadButtonUp: {
        args: [FFIType.i32, FFIType.i32],
        returns: FFIType.bool,
    },
    GetGamepadButtonPressed: {
        args: [],
        returns: FFIType.i32,
    },
    GetGamepadAxisCount: {
        args: [FFIType.i32],
        returns: FFIType.i32,
    },
    GetGamepadAxisMovement: {
        args: [FFIType.i32, FFIType.i32],
        returns: FFIType.f32,
    },
    SetGamepadMappings: {
        args: [FFIType.cstring],
        returns: FFIType.i32,
    },
    SetGamepadVibration: {
        args: [FFIType.i32, FFIType.f32, FFIType.f32, FFIType.f32],
        returns: FFIType.void,
    },
    IsMouseButtonPressed: {
        args: [FFIType.i32],
        returns: FFIType.bool,
    },
    IsMouseButtonDown: {
        args: [FFIType.i32],
        returns: FFIType.bool,
    },
    IsMouseButtonReleased: {
        args: [FFIType.i32],
        returns: FFIType.bool,
    },
    IsMouseButtonUp: {
        args: [FFIType.i32],
        returns: FFIType.bool,
    },
    GetMouseX: {
        args: [],
        returns: FFIType.i32,
    },
    GetMouseY: {
        args: [],
        returns: FFIType.i32,
    },
    GetMousePosition: {
        args: [],
        returns: FFIType.ptr,
    },
    GetMouseDelta: {
        args: [],
        returns: FFIType.ptr,
    },
    SetMousePosition: {
        args: [FFIType.i32, FFIType.i32],
        returns: FFIType.void,
    },
    SetMouseOffset: {
        args: [FFIType.i32, FFIType.i32],
        returns: FFIType.void,
    },
    SetMouseScale: {
        args: [FFIType.f32, FFIType.f32],
        returns: FFIType.void,
    },
    GetMouseWheelMove: {
        args: [],
        returns: FFIType.f32,
    },
    GetMouseWheelMoveV: {
        args: [],
        returns: FFIType.ptr,
    },
    SetMouseCursor: {
        args: [FFIType.i32],
        returns: FFIType.void,
    },
    GetTouchX: {
        args: [],
        returns: FFIType.i32,
    },
    GetTouchY: {
        args: [],
        returns: FFIType.i32,
    },
    GetTouchPosition: {
        args: [FFIType.i32],
        returns: FFIType.ptr,
    },
    GetTouchPointId: {
        args: [FFIType.i32],
        returns: FFIType.i32,
    },
    GetTouchPointCount: {
        args: [],
        returns: FFIType.i32,
    },
    SetGesturesEnabled: {
        args: [FFIType.i32],
        returns: FFIType.void,
    },
    IsGestureDetected: {
        args: [FFIType.i32],
        returns: FFIType.bool,
    },
    GetGestureDetected: {
        args: [],
        returns: FFIType.i32,
    },
    GetGestureHoldDuration: {
        args: [],
        returns: FFIType.f32,
    },
    GetGestureDragVector: {
        args: [],
        returns: FFIType.ptr,
    },
    GetGestureDragAngle: {
        args: [],
        returns: FFIType.f32,
    },
    GetGesturePinchVector: {
        args: [],
        returns: FFIType.ptr,
    },
    GetGesturePinchAngle: {
        args: [],
        returns: FFIType.f32,
    },
    UpdateCamera: {
        args: [FFIType.ptr, FFIType.i32],
        returns: FFIType.void,
    },
    UpdateCameraPro: {
        args: [FFIType.ptr, FFIType.ptr, FFIType.ptr, FFIType.f32],
        returns: FFIType.void,
    },


    LoadImage: {
        args: [FFIType.cstring],
        returns: FFIType.ptr,
    }
})